{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "7b11e9bc",
   "metadata": {},
   "source": [
    "# Robotics, Vision & Control 3e: for Python\n",
    "## Chapter 11: Images & Image processing\n",
    "\n",
    "Copyright (c) 2021- Peter Corke"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b60ebcc6",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    import google.colab\n",
    "    print('Running on CoLab')\n",
    "    !pip install matplotlib\n",
    "    !pip install machinevision-toolbox-python\n",
    "    COLAB = True\n",
    "except:\n",
    "    COLAB = False\n",
    "\n",
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"last_expr_or_assign\"\n",
    "\n",
    "import numpy as np\n",
    "%matplotlib widget\n",
    "import matplotlib.pyplot as plt\n",
    "import math\n",
    "from math import pi\n",
    "from os import getenv\n",
    "np.set_printoptions(\n",
    "    linewidth=120, formatter={\n",
    "        'float': lambda x: f\"{0:8.4g}\" if abs(x) < 1e-10 else f\"{x:8.4g}\"})\n",
    "np.random.seed(0)\n",
    "from machinevisiontoolbox.base import *\n",
    "from machinevisiontoolbox import *\n",
    "from spatialmath.base import *\n",
    "from spatialmath import SE2, Twist2\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3fd3d55",
   "metadata": {},
   "source": [
    "# 11.1 Obtaining an Image\n",
    "## 11.1.1 Images from Files\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa78a62d",
   "metadata": {},
   "outputs": [],
   "source": [
    "street, _ = iread(\"street.png\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ef774a53",
   "metadata": {},
   "outputs": [],
   "source": [
    "street"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cdb74220",
   "metadata": {},
   "outputs": [],
   "source": [
    "street.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "244e02e0",
   "metadata": {},
   "outputs": [],
   "source": [
    "street[400, 200]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f93ad85c",
   "metadata": {},
   "outputs": [],
   "source": [
    "idisp(street);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "efc97a94",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers, _ = iread(\"flowers8.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "45cb88a2",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12f59d8c",
   "metadata": {},
   "outputs": [],
   "source": [
    "idisp(flowers);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb7dd43e",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87d09b36",
   "metadata": {},
   "outputs": [],
   "source": [
    "idisp(flowers[:, :, 0]);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1ec24372",
   "metadata": {},
   "outputs": [],
   "source": [
    "pix = flowers[276, 318, :]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6480e577",
   "metadata": {},
   "outputs": [],
   "source": [
    "street = Image.Read(\"street.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e20d4a56",
   "metadata": {},
   "outputs": [],
   "source": [
    "street.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "882ec4ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "street.disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "efef79c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "street.min()\n",
    "street.max()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4ecd283",
   "metadata": {},
   "outputs": [],
   "source": [
    "street.stats()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c479f73",
   "metadata": {},
   "outputs": [],
   "source": [
    "img = street.image;\n",
    "type(img)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "64f747ef",
   "metadata": {},
   "outputs": [],
   "source": [
    "street.image[400, 200]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "547b3efc",
   "metadata": {},
   "outputs": [],
   "source": [
    "subimage = street.image[100:200, 200:300];\n",
    "type(subimage)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0a69c66c",
   "metadata": {},
   "outputs": [],
   "source": [
    "subimage = street[100:200, 200:300];\n",
    "type(subimage)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1541b5ca",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers = Image.Read(\"flowers8.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4f358094",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers.stats()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b44d3bf2",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers.image[276, 318, :]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2c568b37",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers.image[:, :, 0];"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "513ace5c",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers[:, :, 0].disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc4fdca6",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers.plane(0).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "644cc54f",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers.plane(\"R\").disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9529f1ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers.red().disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8bf8b224",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers.plane(\"B:R:G\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7483ec40",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers.plane(\"B:G:R\").disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c54636dc",
   "metadata": {},
   "outputs": [],
   "source": [
    "church = Image.Read(\"church.jpg\");\n",
    "church.metadata()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f1a34fe",
   "metadata": {},
   "source": [
    "## 11.1.2 Images from File Sequences\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8a466438",
   "metadata": {},
   "outputs": [],
   "source": [
    "images = ImageCollection(\"seq/*.png\");\n",
    "len(images)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "571ecfed",
   "metadata": {},
   "outputs": [],
   "source": [
    "images[3]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "ca4fb0c1",
   "metadata": {},
   "source": [
    "<div style=\"background-color:red\">\n",
    "<span style=\"background-color:red; font-size:20pt\">NOTE</span>\n",
    "\n",
    "The commented out code below produces an animation for a Python script, however, using Jupyter produces a set of separate images.  Use OpenCV instead (`matplotlib=False`) to display the animation in a separate window.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "90d768d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# for image in images:\n",
    "#   image.disp(reuse=True, fps=5)  # do some operation\n",
    "\n",
    "# use OpenCV instead\n",
    "if COLAB:\n",
    "  print(\"this example cannot run with CoLab\")\n",
    "else:\n",
    "  for image in images:\n",
    "    image.disp(title=\"sequence\", reuse=True, fps=5, matplotlib=False)  # do some operation\n",
    "  \n",
    "  # type 'q' in the image animation window to close it\n",
    "  cv_destroy_window(\"sequence\", block=False)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "a60e0bd8",
   "metadata": {},
   "source": [
    "<div style=\"background-color:red\">\n",
    "<span style=\"background-color:red; font-size:20pt\">NOTE</span>\n",
    "\n",
    "The example below requires that you have downloaded the extra image data, [see these instructions](https://github.com/petercorke/machinevision-toolbox-python/blob/master/mvtb-data/README.md#install-big-image-files).  These files will not be installed by default when you run on CoLab.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8d269999",
   "metadata": {},
   "outputs": [],
   "source": [
    "if not COLAB:\n",
    "    images = ZipArchive(\"bridge-l.zip\", \"*.pgm\");\n",
    "    len(images)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa5fa245",
   "metadata": {},
   "source": [
    "## 11.1.3 Images from an Attached Camera\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c40c67f",
   "metadata": {},
   "outputs": [],
   "source": [
    "if COLAB:\n",
    "    print(\"there is no camera available in the Colab environment\")\n",
    "else:\n",
    "    camera = VideoCamera(0)\n",
    "    image = camera.grab()\n",
    "    camera.release()\n",
    "    image.disp()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e8e1b0b",
   "metadata": {},
   "source": [
    "## 11.1.4 Images from a Video File\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "56ed0508",
   "metadata": {},
   "outputs": [],
   "source": [
    "video = VideoFile(\"traffic_sequence.mp4\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2e978042",
   "metadata": {},
   "outputs": [],
   "source": [
    "video.shape"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "706458ab",
   "metadata": {},
   "source": [
    "<div style=\"background-color:red\">\n",
    "<span style=\"background-color:red; font-size:20pt\">NOTE</span>\n",
    "\n",
    "The commented out code produces an animation for a Python script, however, using Jupyter produces a set of separate images.  Use OpenCV instead (`matplotlib=False`) to display the animation in a separate window.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5c3f037f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# for frame in video:\n",
    "#   frame.disp(reuse=True, fps=video.fps) # display frame in the same axes\n",
    "\n",
    "# use OpenCV instead\n",
    "if COLAB:\n",
    "  print(\"this example cannot run with CoLab\")\n",
    "else:\n",
    "  for frame in video:\n",
    "    frame.disp(title=\"intersection\", reuse=True, fps=video.fps, matplotlib=False) # display frame in the same axes\n",
    "    # type 'q' in the image animation window to close it\n",
    "  cv_destroy_window(\"intersection\", block=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a941ca8d",
   "metadata": {},
   "source": [
    "## 11.1.5 Images from the Web\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3653181",
   "metadata": {},
   "outputs": [],
   "source": [
    "# dartmouth = WebCam(\"https://webcam.dartmouth.edu/webcam/image.jpg\");\n",
    "# dartmouth.grab().disp();\n",
    "\n",
    "# the camera above has worked since the first edition of the book, but is now not\n",
    "# responding, here's a camera from Porjus, in Lappland, Sweden.\n",
    "porjus= WebCam(\"http://uk.jokkmokk.jp/photo/nr4/latest.jpg\");\n",
    "porjus.grab().disp();\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4ecb567",
   "metadata": {},
   "source": [
    "## 11.1.6 Images from Space\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eda10f69",
   "metadata": {},
   "outputs": [],
   "source": [
    "if COLAB:\n",
    "    # you must provide a Google API key, a 39 character string\n",
    "    world = EarthView(key=\"API key\");\n",
    "else:\n",
    "    # you're Google API key must be provided as the envariable GOOGLE_KEY\n",
    "    world = EarthView()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1b3b0ece",
   "metadata": {},
   "outputs": [],
   "source": [
    "world.grab(-27.475722, 153.0285, 17).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b234c47c",
   "metadata": {},
   "outputs": [],
   "source": [
    "world.grab(-27.475722,153.0285, 15, type=\"map\").disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e77ad3ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "world.grab(-27.475722,153.0285, 15, type=\"roads\").disp();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e118ce7",
   "metadata": {},
   "source": [
    "## 11.1.7 Images from Code\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c3146fd3",
   "metadata": {},
   "outputs": [],
   "source": [
    "image = Image.Ramp(cycles=2, size=500, dir=\"x\");\n",
    "image = Image.Sin(cycles=5, size=500, dir=\"y\");\n",
    "image = Image.Squares(number=5, size=500);\n",
    "image = Image.Circles(number=2, size=500);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9012f4c8",
   "metadata": {},
   "outputs": [],
   "source": [
    "canvas = Image.Zeros(1000, 1000, dtype=\"uint8\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc1655f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "canvas.draw_box(lt=(100, 100), wh=(150, 150), color=100, thickness=-1);\n",
    "canvas.draw_box(lt=(300, 300), wh=(80, 80), color=150, thickness=-1);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "941a1c00",
   "metadata": {},
   "outputs": [],
   "source": [
    "canvas.draw_circle((600, 600), 120, color=200, thickness=-1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c82562b1",
   "metadata": {},
   "outputs": [],
   "source": [
    "canvas.draw_line((100, 100), (800, 800), color=250, thickness=8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "89cf69e4",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "canvas.disp();"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "1544dfda",
   "metadata": {},
   "source": [
    "#  11.2 Pixel Value Distribution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e611b9d5",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.close('all')\n",
    "church = Image.Read(\"church.png\", mono=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6313fb92",
   "metadata": {},
   "outputs": [],
   "source": [
    "church.min()\n",
    "church.max()\n",
    "church.mean()\n",
    "church.median()\n",
    "church.std()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7a755f4b",
   "metadata": {},
   "outputs": [],
   "source": [
    "church.stats()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "78c13b7f",
   "metadata": {},
   "outputs": [],
   "source": [
    "h = church.hist()\n",
    "h.plot();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b90e5c7a",
   "metadata": {},
   "outputs": [],
   "source": [
    "h.plot(\"ncdf\", color=\"blue\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a9b54525",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = h.peaks();\n",
    "x.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ab8f32ba",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = h.peaks(scale=25)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28621e3a",
   "metadata": {},
   "source": [
    "# 11.3 Monadic Operations\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ddfe76b6",
   "metadata": {},
   "outputs": [],
   "source": [
    "church_float = church.to(\"float\")\n",
    "church.max()\n",
    "church_float.max()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c6d3684e",
   "metadata": {},
   "outputs": [],
   "source": [
    "church_float.to(\"uint8\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5a17198d",
   "metadata": {},
   "outputs": [],
   "source": [
    "street_float = Image.Read(\"street.png\", dtype=\"float\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2f9d2a31",
   "metadata": {},
   "outputs": [],
   "source": [
    "gray = flowers.mono()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "182593f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "color = gray.colorize()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b0ad350d",
   "metadata": {},
   "outputs": [],
   "source": [
    "color = gray.colorize((1, 0, 0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7b6024f3",
   "metadata": {},
   "outputs": [],
   "source": [
    "bright = (church >= 180)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7ceb7ea4",
   "metadata": {},
   "outputs": [],
   "source": [
    "bright.disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f85a6bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "church.stretch().stats()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e9af62da",
   "metadata": {},
   "outputs": [],
   "source": [
    "im = church.normhist();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d54db3f4",
   "metadata": {},
   "outputs": [],
   "source": [
    "im = church.gamma_decode(1 / 2.2);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f0be214a",
   "metadata": {},
   "outputs": [],
   "source": [
    "im = church.gamma_decode(\"sRGB\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d1aa1289",
   "metadata": {},
   "outputs": [],
   "source": [
    "(church // 64).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e86c6588",
   "metadata": {},
   "outputs": [],
   "source": [
    "church.apply(lambda x: x // 64, vectorize=True).disp()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14b82f35",
   "metadata": {},
   "outputs": [],
   "source": [
    "church.npixels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bcc90409",
   "metadata": {},
   "outputs": [],
   "source": [
    "church.apply(lambda x: x // 64).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fce4d467",
   "metadata": {},
   "outputs": [],
   "source": [
    "lut = [x // 64 for x in range(256)];  # create posterization lookup table\n",
    "church.LUT(lut).disp();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ce09725",
   "metadata": {},
   "source": [
    "# 11.4 Dyadic Operations\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8b60e5bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "church / 2       # new Image with all pixel values halved\n",
    "church + 20      # new Image with all pixel values increased by 20\n",
    "church - church  # new Image with all pixel values equal to 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "38b7ad70",
   "metadata": {},
   "outputs": [],
   "source": [
    "church.apply2(church, lambda x, y: x - y, vectorize=True).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6600e6ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "church.apply2(church, lambda x, y: x - y).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "acb0f18a",
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.uint8(100)\n",
    "b = np.uint8(200)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98b73a09",
   "metadata": {},
   "outputs": [],
   "source": [
    "a + b\n",
    "a - b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40fdcc93",
   "metadata": {},
   "outputs": [],
   "source": [
    "-a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4373f5f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "a / b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "154ee5d5",
   "metadata": {},
   "source": [
    "## 11.4.1 Applications\n",
    "### 11.4.1.1 Application: Chroma Keying\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8b74d3f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "foreground = Image.Read(\"greenscreen.png\", dtype=\"float\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af783e09",
   "metadata": {},
   "outputs": [],
   "source": [
    "cc = foreground.gamma_decode(\"sRGB\").chromaticity()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "44c06f1e",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "cc.plane(\"g\").hist().plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "848d9942",
   "metadata": {},
   "outputs": [],
   "source": [
    "mask = cc.plane(\"g\") < 0.45;\n",
    "mask.disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "778b152a",
   "metadata": {},
   "outputs": [],
   "source": [
    "(foreground * mask).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "90d0f39f",
   "metadata": {},
   "outputs": [],
   "source": [
    "background = Image.Read(\"road.png\", dtype=\"float\").samesize(foreground)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1a379f33",
   "metadata": {},
   "outputs": [],
   "source": [
    "(background * ~mask).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98698eac",
   "metadata": {},
   "outputs": [],
   "source": [
    "composite = foreground * mask  + background * ~mask;\n",
    "composite.disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99b50433",
   "metadata": {},
   "outputs": [],
   "source": [
    "background.choose(foreground, mask).disp();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1daab42e",
   "metadata": {},
   "source": [
    "### 11.4.1.2 Application: Motion detection\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16d59bd5",
   "metadata": {},
   "outputs": [],
   "source": [
    "video = VideoFile(\"traffic_sequence.mp4\", mono=True, dtype=\"float\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3b08a7ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "if COLAB:\n",
    "  print(\"this example cannot run with CoLab\")\n",
    "else:\n",
    "  sigma = 0.02;\n",
    "  background = None\n",
    "  for im in video:\n",
    "    if background is None:\n",
    "      background = im  # first frame only\n",
    "    else:\n",
    "      d = im - background\n",
    "      background += d.clip(-sigma, sigma)\n",
    "    # background.disp(reuse=True, fps=video.fps, block=None)\n",
    "    background.disp(title=\"motion detection\", reuse=True, fps=video.fps, matplotlib=False)\n",
    "  cv_destroy_window(\"motion detection\", block=False)\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "20fce3de",
   "metadata": {},
   "source": [
    "<div style=\"background-color:red\">\n",
    "<span style=\"background-color:red; font-size:20pt\">NOTE</span>\n",
    "\n",
    "The commented out code produces an animation for a Python script, however, using Jupyter it produces a set of separate images.  Use OpenCV instead (`matplotlib=False`) to display the animation in a separate window.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3488c620",
   "metadata": {},
   "source": [
    "# 11.5 Spatial Operations\n",
    "## 11.5.1 Linear Spatial Filtering\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27e6a10b",
   "metadata": {},
   "source": [
    "O = I.convolve(K);"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "4c83dd01",
   "metadata": {},
   "source": [
    "### 11.5.1.1 Image Smoothing\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "362391c8",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.close('all')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34962fde",
   "metadata": {},
   "outputs": [],
   "source": [
    "K = Image.Constant(21, 21, value=1/21**2);\n",
    "K.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1c88e9eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "mona = Image.Read(\"monalisa.png\", mono=True, dtype=\"float\")\n",
    "mona.convolve(K).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f3fd8957",
   "metadata": {},
   "outputs": [],
   "source": [
    "K = Kernel.Box(h=10);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "46466119",
   "metadata": {},
   "outputs": [],
   "source": [
    "K = Kernel.Gauss(sigma=5);\n",
    "mona.convolve(K).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b76b05b8",
   "metadata": {},
   "outputs": [],
   "source": [
    "K.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f14260b1",
   "metadata": {},
   "outputs": [],
   "source": [
    "mona.smooth(sigma=5).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bcca09b4",
   "metadata": {},
   "outputs": [],
   "source": [
    "idisp(K);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7e00ea92",
   "metadata": {},
   "outputs": [],
   "source": [
    "span = np.arange(-15, 15 + 1);\n",
    "X, Y = np.meshgrid(span, span)\n",
    "plt.subplot(projection=\"3d\").plot_surface(X, Y, K);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c94853db",
   "metadata": {},
   "outputs": [],
   "source": [
    "K = Kernel.Circle(radius=8, h=15);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2ee8f786",
   "metadata": {},
   "outputs": [],
   "source": [
    "K=Kernel.Box(h=1)\n",
    "np.linalg.matrix_rank(K)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "367fbb9d",
   "metadata": {},
   "outputs": [],
   "source": [
    "U, s, Vh  = np.linalg.svd(K, full_matrices=True)\n",
    "Kh = s[0] * U[:, 0]  # 1D horizontal kernel\n",
    "Kv = Vh[0, :]        # 1D vertical kernel"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3e220bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "np.outer(Kh, Kv) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87b69170",
   "metadata": {},
   "source": [
    "### 11.5.1.2 Border Extrapolation\n",
    "### 11.5.1.3 Edge Detection\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "036bdd5f",
   "metadata": {},
   "outputs": [],
   "source": [
    "castle = Image.Read(\"castle.png\", mono=True, dtype=\"float\")\n",
    "castle.disp()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bef135d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "profile = castle.image[360, :];\n",
    "profile.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5fce8388",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.plot(profile);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ab7c4a67",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.plot(np.diff(profile));"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4cf37716",
   "metadata": {},
   "outputs": [],
   "source": [
    "K = [0.5, 0, -0.5];\n",
    "castle.convolve(K).disp(colormap=\"signed\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "35676826",
   "metadata": {},
   "outputs": [],
   "source": [
    "Du = Kernel.Sobel()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc559954",
   "metadata": {},
   "outputs": [],
   "source": [
    "castle.convolve(Du).disp(colormap=\"signed\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "336d86f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "castle.convolve(Du.T).disp(colormap=\"signed\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "031b9022",
   "metadata": {},
   "outputs": [],
   "source": [
    "from scipy.signal import convolve2d\n",
    "Gu = convolve2d(Du, Kernel.Gauss(sigma=1));\n",
    "Gu.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ede9975f",
   "metadata": {},
   "outputs": [],
   "source": [
    "Xu = castle.convolve(Kernel.DGauss(sigma=2));\n",
    "Xv = castle.convolve(Kernel.DGauss(sigma=2).T);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fbfe41b4",
   "metadata": {},
   "outputs": [],
   "source": [
    "m = (Xu ** 2 + Xv ** 2).sqrt()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a12b805c",
   "metadata": {},
   "outputs": [],
   "source": [
    "th = Xv.apply2(Xu, np.arctan2);  # arctan2(Xv, Xu)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "50fa37fa",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "plt.quiver(castle.uspan(20), castle.vspan(20),\n",
    "  Xu.image[::20, ::20], Xv.image[::20, ::20], scale=10);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b8ffc3ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "Xu, Xv = castle.gradients(Kernel.DGauss(sigma=2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ddb0acd3",
   "metadata": {},
   "outputs": [],
   "source": [
    "edges = castle.canny()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bac30eee",
   "metadata": {},
   "outputs": [],
   "source": [
    "L = Kernel.Laplace()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6e5675d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "lap = castle.convolve(Kernel.LoG(sigma=2));"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19f6fc67",
   "metadata": {},
   "outputs": [],
   "source": [
    "profile = lap.image[360, 570:601];\n",
    "plt.figure()\n",
    "plt.plot(np.arange(570, 601), profile, \"-o\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffebfe8a",
   "metadata": {},
   "outputs": [],
   "source": [
    "zc = lap.zerocross();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f02411c9",
   "metadata": {},
   "source": [
    "## 11.5.2 Template Matching\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82e3387c",
   "metadata": {},
   "outputs": [],
   "source": [
    "mona = Image.Read(\"monalisa.png\", mono=True, dtype=\"float\");\n",
    "A = mona.roi([170, 220, 245, 295]);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "052b9106",
   "metadata": {},
   "outputs": [],
   "source": [
    "B = A\n",
    "A.sad(B)\n",
    "A.ssd(B)\n",
    "A.ncc(B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b06fb425",
   "metadata": {},
   "outputs": [],
   "source": [
    "B = 0.9 * A\n",
    "A.sad(B)\n",
    "A.ssd(B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1ed7e0ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "A.ncc(B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2886a185",
   "metadata": {},
   "outputs": [],
   "source": [
    "B = A + 0.1\n",
    "A.sad(B)\n",
    "A.ssd(B)\n",
    "A.ncc(B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40f91bea",
   "metadata": {},
   "outputs": [],
   "source": [
    "A.zsad(B)\n",
    "A.zssd(B)\n",
    "A.zncc(B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb7981bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "B = 0.9 * A + 0.1\n",
    "A.zncc(B)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4e41da8",
   "metadata": {},
   "source": [
    "### 11.5.2.1 Application: Finding Wally\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0b72c898",
   "metadata": {},
   "outputs": [],
   "source": [
    "crowd = Image.Read(\"wheres-wally.png\", mono=True, dtype=\"float\")\n",
    "crowd.disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "26b262d6",
   "metadata": {},
   "outputs": [],
   "source": [
    "T = Image.Read(\"wally.png\", mono=True, dtype=\"float\")\n",
    "T.disp();\n",
    "T"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bc14a7bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "sim = crowd.similarity(T, \"zncc\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cff5e06a",
   "metadata": {},
   "outputs": [],
   "source": [
    "sim.disp(colormap=\"signed\", colorbar=True);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81676449",
   "metadata": {},
   "outputs": [],
   "source": [
    "maxima, location = sim.peak2d(scale=2, npeaks=5)\n",
    "maxima"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9205cfb8",
   "metadata": {},
   "outputs": [],
   "source": [
    "location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c0b1d92e",
   "metadata": {},
   "outputs": [],
   "source": [
    "crowd.disp(block=None);\n",
    "location += np.c_[T.shape[::-1]].T // 2  # offset to account for smaller size of sim image\n",
    "plot_circle(centre=location, radius=20, color=\"k\");\n",
    "plot_point(location, color=\"none\", marker=\"none\", text=\"  #{}\");"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "793b56fd",
   "metadata": {},
   "source": [
    "<div style=\"background-color:red\">\n",
    "<span style=\"background-color:red; font-size:20pt\">NOTE</span>\n",
    "\n",
    "In the book this image couldn't be published because I couldn't get a copyright release, in fact I couldn't even find out who is actually the copyright holder.\n",
    "\n",
    "The image fron the similarity matching is smaller than the input image.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3394a1fc",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(crowd.shape)\n",
    "print(sim.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28ea0786",
   "metadata": {},
   "source": [
    "Pixels where the template crosses the image boundary are not included.  See the [OpenCV documentation](https://docs.opencv.org/4.x/df/dfb/group__imgproc__object.html#ga586ebfb0a7fb604b35a23d85391329be) for details.  To account for this we need to offset the locations of the peaks by the half-width of the template image."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64c50ae5",
   "metadata": {},
   "source": [
    "### 11.5.2.2 Nonparameteric Local Transforms\n",
    "## 11.5.3 Nonlinear Operations\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36e05446",
   "metadata": {},
   "outputs": [],
   "source": [
    "out = mona.window(np.var, h=3);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "89c2489e",
   "metadata": {},
   "outputs": [],
   "source": [
    "mx = mona.rank(rank=0, h=2);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "678e31e2",
   "metadata": {},
   "outputs": [],
   "source": [
    "med = mona.rank(rank=11, h=2);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "638d04b7",
   "metadata": {},
   "outputs": [],
   "source": [
    "spotty = mona.copy()\n",
    "pixels = spotty.view1d();  # create a NumPy 1D view\n",
    "npix = mona.npixels    # total number of pixels\n",
    "k = np.random.choice(npix, 10_000, replace=True);  # choose 10,000 unique pixels\n",
    "pixels[k[:5_000]] = 0  # set half of them to zero\n",
    "pixels[k[5_000:]] = 1  # set half of them to one\n",
    "spotty.disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c4b5f312",
   "metadata": {},
   "outputs": [],
   "source": [
    "spotty.rank(rank=4, h=1).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "716f99ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "M = np.full((3, 3), True);\n",
    "M[1, 1] = False\n",
    "M"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "249f8f8a",
   "metadata": {},
   "outputs": [],
   "source": [
    "max_neighbors = mona.rank(rank=0, footprint=M);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19a3e132",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = plt.subplots(figsize=(10,10))  # display it large\n",
    "(mona > max_neighbors).disp(ax=ax);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c169869",
   "metadata": {},
   "source": [
    "# 11.6 Mathematical Morphology\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "02ae5efe",
   "metadata": {},
   "outputs": [],
   "source": [
    "im = Image.Read(\"eg-morph1.png\")\n",
    "im.disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c92f46a5",
   "metadata": {},
   "outputs": [],
   "source": [
    "S = np.ones((5, 5));"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "00e0b073",
   "metadata": {},
   "outputs": [],
   "source": [
    "e1 = im.morph(S, op=\"min\")\n",
    "e1.disp()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c73889ce",
   "metadata": {},
   "source": [
    "morphdemo(im, S, op=\"min\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4069cc7a",
   "metadata": {},
   "outputs": [],
   "source": [
    "d1 = e1.morph(S, op=\"max\")\n",
    "d1.disp()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b973b36a",
   "metadata": {},
   "outputs": [],
   "source": [
    "out = im.erode(S);\n",
    "out.disp()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4055a65a",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "out = im.dilate(S);\n",
    "out.disp()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c53de855",
   "metadata": {},
   "source": [
    "## 11.6.1 Noise Removal\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a04df10",
   "metadata": {},
   "outputs": [],
   "source": [
    "objects = Image.Read(\"segmentation.png\")\n",
    "objects.disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "001e3a2f",
   "metadata": {},
   "outputs": [],
   "source": [
    "S_circle = Kernel.Circle(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8363f419",
   "metadata": {},
   "outputs": [],
   "source": [
    "closed = objects.close(S_circle);\n",
    "closed.disp()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d90ca981",
   "metadata": {},
   "outputs": [],
   "source": [
    "clean = closed.open(S_circle);\n",
    "clean.disp()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9f07869b",
   "metadata": {},
   "outputs": [],
   "source": [
    "objects.open(S_circle).close(S_circle).disp();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68f229b1",
   "metadata": {},
   "source": [
    "## 11.6.2 Boundary Detection\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8d81c66",
   "metadata": {},
   "outputs": [],
   "source": [
    "eroded = clean.erode(S_circle)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a29176e9",
   "metadata": {},
   "outputs": [],
   "source": [
    "edge = clean - eroded\n",
    "edge.disp();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd48ed14",
   "metadata": {},
   "source": [
    "## 11.6.3 Hit or Miss Transform\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff43a2a0",
   "metadata": {},
   "source": [
    "out = image.hitormiss(S);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "432178c1",
   "metadata": {},
   "outputs": [],
   "source": [
    "skeleton = clean.thin();\n",
    "skeleton.disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24186ad9",
   "metadata": {},
   "outputs": [],
   "source": [
    "ends = skeleton.endpoint()\n",
    "\n",
    "(skeleton + ends.dilate(np.ones((3,3)), n=2)).disp() # use morphology to make the points bigger and more visible"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "70a5aa40",
   "metadata": {},
   "outputs": [],
   "source": [
    "joins = skeleton.triplepoint();\n",
    "\n",
    "(skeleton + joins.dilate(np.ones((3,3)), n=2)).disp() # use morphology to make the points bigger and more visible"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "733796fc",
   "metadata": {},
   "source": [
    "## 11.6.4 Distance Transform\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4dda1561",
   "metadata": {},
   "outputs": [],
   "source": [
    "im = Image.Squares(1, size=256).rotate(0.3).canny()\n",
    "im.disp()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7dffacd3",
   "metadata": {},
   "outputs": [],
   "source": [
    "dx = im.distance_transform(norm=\"L2\")\n",
    "dx.disp()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85fe6d52",
   "metadata": {},
   "source": [
    "# 11.7 Shape Changing\n",
    "## 11.7.1 Cropping\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "04c41495",
   "metadata": {},
   "outputs": [],
   "source": [
    "mona.disp();"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "54caa2e5",
   "metadata": {},
   "source": [
    "We can't do the next cell from inside Jupyter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3bbb6c1e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Select a region of interest on the image, left click then drag\n",
    "# eyes, roi = mona.roi();\n",
    "# eyes\n",
    "# roi"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "073be862",
   "metadata": {},
   "outputs": [],
   "source": [
    "smile = mona.roi([265, 342, 264, 286])\n",
    "smile.disp();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "202c60b5",
   "metadata": {},
   "source": [
    "## 11.7.2 Image Resizing\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "22ee06b3",
   "metadata": {},
   "outputs": [],
   "source": [
    "roof = Image.Read(\"roof.png\", mono=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1d89ee03",
   "metadata": {},
   "outputs": [],
   "source": [
    "roof[::7, ::7].disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "815ea60d",
   "metadata": {},
   "outputs": [],
   "source": [
    "roof.smooth(sigma=3)[::7, ::7].disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7ad461af",
   "metadata": {},
   "outputs": [],
   "source": [
    "smaller = roof.scale(1/7, sigma=3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "48e7fd94",
   "metadata": {},
   "outputs": [],
   "source": [
    "smaller.replicate(7).disp();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e6f7d24",
   "metadata": {},
   "source": [
    "## 11.7.3 Image Pyramids\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2f6f8d2c",
   "metadata": {},
   "outputs": [],
   "source": [
    "pyramid = mona.pyramid()\n",
    "len(pyramid)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19d4cf92",
   "metadata": {},
   "outputs": [],
   "source": [
    "pyramid[0].disp()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53d130cf",
   "metadata": {},
   "outputs": [],
   "source": [
    "pyramid[5].disp()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ebc5859",
   "metadata": {},
   "source": [
    "## 11.7.4 Image Warping\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0c01a1d6",
   "metadata": {},
   "outputs": [],
   "source": [
    "Up, Vp = Image.meshgrid(width=500, height=500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81c0c9bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "U = 4 * (Up - 100);\n",
    "V = 4 * (Vp - 200);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ed5aad19",
   "metadata": {},
   "outputs": [],
   "source": [
    "p = (300, 200);  # (v, u)\n",
    "(U[p], V[p])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "907fdcc7",
   "metadata": {},
   "outputs": [],
   "source": [
    "p = (100, 200);  # (v, u)\n",
    "(U[p], V[p])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f0291510",
   "metadata": {},
   "outputs": [],
   "source": [
    "mona.warp(U, V).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34d7d022",
   "metadata": {},
   "outputs": [],
   "source": [
    "M = np.diag([0.25, 0.25, 1]) * SE2(100, 200) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ec881b55",
   "metadata": {},
   "outputs": [],
   "source": [
    "out = mona.warp_affine(M, bgcolor=np.nan);\n",
    "out.disp(badcolor=\"r\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ccaff6ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "S = Twist2.UnitRevolute(mona.centre)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3a89edc2",
   "metadata": {},
   "outputs": [],
   "source": [
    "M = S.exp(pi / 6)\n",
    "out = mona.warp_affine(M, bgcolor=np.nan)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21c0d766",
   "metadata": {},
   "outputs": [],
   "source": [
    "twisted_mona = mona.rotate(pi/6);\n",
    "twisted_mona.disp();"
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all",
   "main_language": "python",
   "notebook_metadata_filter": "-all"
  },
  "kernelspec": {
   "display_name": "dev",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.9"
  },
  "vscode": {
   "interpreter": {
    "hash": "b7d6b0d76025b9176285a6442c3dd6dd39bcfe7241029b7898b7106bd5e9b472"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
